use crate::db::{dao, po};
use crate::dto;
use crate::dto::core::user::UserType;
use crate::service::Error;
use std::sync::Arc;
use wanmesh_common::dto::ResponseCode;
use wanmesh_common::error::{ResponseError, ResponseErrorAny};

#[async_trait::async_trait]
pub trait UserService: Sync + Send {
    async fn try_get(&self, username: &str) -> Result<Option<dto::core::user::User>, Error>;
    async fn exists(&self, username: &str) -> Result<bool, Error>;
    async fn verify_password(&self, username: &str, password: &str) -> Result<(), Error>;
}

pub struct DefaultUserService {
    user_dao: Arc<dyn dao::user::UserDao>,
}

impl DefaultUserService {
    pub fn new(user_dao: Arc<dyn dao::user::UserDao>) -> Self {
        DefaultUserService { user_dao }
    }
}

#[async_trait::async_trait]
impl UserService for DefaultUserService {
    async fn try_get(&self, username: &str) -> Result<Option<dto::core::user::User>, Error> {
        match self
            .user_dao
            .try_get(dao::user::Filter {
                username: Some(username.to_string()),
                ..Default::default()
            })
            .await?
        {
            None => Ok(None),
            Some(v) => Ok(Some(dto::core::user::User::from(&v))),
        }
    }

    async fn exists(&self, username: &str) -> Result<bool, Error> {
        let exists = self
            .user_dao
            .exists(dao::user::Filter {
                username: Some(username.to_string()),
                ..Default::default()
            })
            .await?;

        Ok(exists)
    }
    async fn verify_password(&self, username: &str, password: &str) -> Result<(), Error> {
        let user = self
            .user_dao
            .try_get(dao::user::Filter {
                username: Some(username.to_string()),
                ..Default::default()
            })
            .await?
            .ok_or(err_user_not_found(username))?;

        if let Err(e) =
            wanmesh_common::auth::verify_password(password, user.password_crypto.as_str())
        {
            return Err(Error::ResponseErrorAny(ResponseErrorAny {
                code: ResponseCode::UNAUTHORIZED,
                message: format!("密码错误，username={}", username),
                source: e,
            }));
        }
        Ok(())
    }
}

fn err_user_not_found(username: &str) -> ResponseError {
    ResponseError {
        code: ResponseCode::NOT_FOUND,
        message: format!("用户不存在，username={}", username),
        backtrace: Default::default(),
    }
}

impl From<&po::user::Model> for dto::core::user::User {
    fn from(value: &po::user::Model) -> Self {
        Self {
            user_id: value.id.to_string(),
            user_type: UserType::from(value.user_type),
            username: value.username.clone(),
            description: value.description.clone(),
        }
    }
}
